package com.witsoft.device.config;

import com.alibaba.fastjson.JSONObject;
import com.witsoft.device.entity.*;
import com.witsoft.device.enums.MachineStatusEnum;
import com.witsoft.device.service.*;
import com.witsoft.device.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.util.*;
import static com.witsoft.device.utils.DateUtil.sdf_s;

@Slf4j
@Configuration
@Component
@EnableScheduling
public class ScheduledTask{

    @Resource
    private DeviceService deviceService;

    @Resource
    private DeviceMetricsLogService deviceMetricsLogService;

    @Resource
    private DeviceStatusTimeLineService deviceStatusTimeLineService;

    @Resource
    private DeviceReporterService reporterService;

    @Resource
    private DeviceDayReporterService dayReporterService;




    /**
     * @desc 设备指标计算，每天凌晨00：00：00执行一次上一日的指标计算，主要解决一些设备每日不停机，无法计算开机时间问题
     * @return
     */
    @Scheduled(cron = "0 0 0 * * ?")
    public void calcMetricsOfDay() {
        //获取当前时间的前一秒，就是昨天23：59：59
        Date yesterday = new Date(System.currentTimeMillis() - 1000);
        Date now = new Date();
        log.info("手动计算前一日设备运行和开机时间：{}", sdf_s.format(yesterday));

        try{
            List<DeviceEntity> deviceList = deviceService.getAllList();
            if(!CollectionUtils.isEmpty(deviceList)){
                List<DeviceStatusTimeLineEntity> add = new ArrayList<>();
                List<DeviceStatusTimeLineEntity> update = new ArrayList<>();

                List<DeviceReporterDay> days = new ArrayList<>();

                //获取mes的所有设备的节拍数和报工时长的乘积
                String deviceCodes = reporterService.getDeviceCodes(deviceList);
                Date start = DateUtil.getCurrentZero(yesterday);
                Map<String, Double> workReportOfDay = deviceService.getBeatMultiplyWorkReportOfDay(deviceCodes, start, yesterday, "day");


                for (DeviceEntity entity : deviceList) {

                    log.info("设备：【{}】 正在手动关机中..., 当前状态：{}", entity.getName(), entity.getStatus());

                    //fixed(2021.12.13): 只有运行的设备（设备表中不存开机状态）才进行状态变更，否则会导致原本关机的设备，第二天被强制赋为运行状态
                    //fixed(2022.01.08): 补充完善：待机的设备一样要进行状态变更，否则会造成前一天的设备开机时长统计不完整，使得开机时长小于待机时长
                    if(MachineStatusEnum.RUNNING.getCodeStr().equals(entity.getStatus()) || MachineStatusEnum.WAITING.getCodeStr().equals(entity.getStatus())){

                        //步骤一：处理昨天的设备运行时长和开机时长
                        //1-1：构造每个设备的强制关机事件模拟
                        DeviceStatusTimeLineEntity stopEvent = new DeviceStatusTimeLineEntity();
                        stopEvent.setStartTime(yesterday);
                        stopEvent.setStatus(MachineStatusEnum.STOPPING.getCodeStr());
                        stopEvent.setDeviceId(entity.getId());
                        stopEvent.setId(UUID.randomUUID().toString());
                        add.add(stopEvent);

                        //1-2：更新昨天每个设备的running和开机时长
                        DeviceStatusTimeLineEntity turnEvent = deviceStatusTimeLineService.getLastOneInfoByStatusYesterday(entity.getId(), MachineStatusEnum.TURNING.getCodeStr());
                        if(null != turnEvent && ObjectUtils.isEmpty(turnEvent.getTotalSpent())){
                            Long leftSeconds = DateUtil.getLeftSeconds(turnEvent.getStartTime(), yesterday);
                            turnEvent.setTotalSpent(leftSeconds);
                            turnEvent.setEndTime(yesterday);
                            update.add(turnEvent);
                        }
                        //1-3：更新上一日的运行时间长
                        DeviceStatusTimeLineEntity runningEvent = deviceStatusTimeLineService.getLastOneInfoByStatusYesterday(entity.getId(), MachineStatusEnum.RUNNING.getCodeStr());
                        if(null != runningEvent && ObjectUtils.isEmpty(runningEvent.getTotalSpent())){
                            Long leftSeconds = DateUtil.getLeftSeconds(runningEvent.getStartTime(), yesterday);
                            runningEvent.setTotalSpent(leftSeconds);
                            runningEvent.setEndTime(yesterday);
                            update.add(runningEvent);
                        }


                        //步骤二：处理今天的设备开机和运行事件
                        //2-1：将昨天手动停机的设备开机
                        //今日开机事件
                        DeviceStatusTimeLineEntity turnEventNow = new DeviceStatusTimeLineEntity();
                        turnEventNow.setStartTime(now);
                        turnEventNow.setStatus(MachineStatusEnum.TURNING.getCodeStr());
                        turnEventNow.setDeviceId(entity.getId());
                        turnEventNow.setId(UUID.randomUUID().toString());
                        add.add(turnEventNow);

                        //今日运行事件
                        DeviceStatusTimeLineEntity runningEventNow = new DeviceStatusTimeLineEntity();
                        runningEventNow.setStartTime(now);
                        runningEventNow.setStatus(MachineStatusEnum.RUNNING.getCodeStr());
                        runningEventNow.setDeviceId(entity.getId());
                        runningEventNow.setId(UUID.randomUUID().toString());
                        add.add(runningEventNow);
                    }
                }

                deviceStatusTimeLineService.saveBatch(add);
                deviceStatusTimeLineService.saveOrUpdateBatch(update);


                //########################################################### step-2: 指标计算 -----> 必须要在上面的进程结束之后计算，否则连续运行几天不停机的设备会统计失误
                for(DeviceEntity deviceEntity : deviceList){
                    Double beanMulReportNum = workReportOfDay.get(deviceEntity.getName());
                    //fixed(2022.02.21): mes获取透传的节拍和报工数乘积，mes改成以分钟为单位，这里需要适配一下
                    beanMulReportNum = beanMulReportNum * 60;
                    calculateDeviceReporterByDay(deviceEntity, days, yesterday, beanMulReportNum);
                }

                dayReporterService.saveBatch(days);

            }
        }catch (Exception e){
            log.error("手动补充设备事件异常：{}", e.getMessage());
        }
    }


    /**
     * @desc 计算设备每天的指标
     * @param deviceEntity ：设备实体
     * @param days ： 天数
     * @param yesterday ：计算时长
     * @param beanMulReportNum 当前设备的生产节拍和报工时长数乘积
     */
    private void calculateDeviceReporterByDay(DeviceEntity deviceEntity, List<DeviceReporterDay> days, Date yesterday, Double beanMulReportNum) {

        //获取前一天的总运行时长
        Long runningTimeDay = deviceStatusTimeLineService.getSumRunningTimeByDayTime(deviceEntity.getId(), DateUtil.sdf_d.format(yesterday));
        Long sumTime = deviceStatusTimeLineService.getSumOpenningTimeByDayTime(deviceEntity.getId(), DateUtil.sdf_d.format(yesterday));

        //fixed(2021.12.03):
        DeviceReporterDay deviceReporterDay = new DeviceReporterDay(runningTimeDay, sumTime, yesterday, deviceEntity.getId());

        //feature(2022.01.11): 增加当日生产总数
        deviceReporterDay.setNumberOfProducts(deviceEntity.getTotalCount());

        Double rate = 0.0, timeRate = 0.0;

        //计算性能稼动率
        if(runningTimeDay > 0){
            rate = beanMulReportNum / runningTimeDay;
        }
        deviceReporterDay.setPerformanceGrainMoveRate(100 * rate);


        if(sumTime > 0){
            timeRate = 1.0 * runningTimeDay / sumTime;
        }
        deviceReporterDay.setTimeGrainMoveRate(100 * timeRate);

        days.add(deviceReporterDay);
    }


    /**
     * 设备月度指标计算，每月第一天执行上个月的报表统计
     *
     * @return
     */
    @Scheduled(cron = "0 0 0 1 * ?")
    public void calcMetricsOfMonth() {
        List<DeviceEntity> deviceList = deviceService.getAllList();
        List<DeviceReporterMonth> reporterMonths = new ArrayList<>();

        if(!CollectionUtils.isEmpty(deviceList)) {
            for (DeviceEntity deviceEntity : deviceList) {
                //获取上个月的月份格式：2021-12
                String lastMonth = DateUtil.sdf.format(DateUtil.getLastMonth());

                Long sumRunningTimeMonth = deviceStatusTimeLineService.getSumRunningTimeLastMonth(deviceEntity.getId());
                Long sumOPenningMonth = deviceStatusTimeLineService.getSumOpeningLastMonth(deviceEntity.getId());

                //fixed(2021.11.18): 修复报表统计上个月的数据，时间却是当月的日期的bug
                DeviceReporterMonth deviceReporterMonth = new DeviceReporterMonth(sumRunningTimeMonth, sumOPenningMonth, DateUtil.getLastMonth(), deviceEntity.getId());

                //查询天维度表，统计当月总的稼动率之和
                //性能稼动率
                Double performanceGrainRateMonth = dayReporterService.getSumPerformanceGrainRateMonthById(deviceEntity.getId(), lastMonth);
                deviceReporterMonth.setPerformanceGrainMoveRate(performanceGrainRateMonth);

                //时间稼动率
                Double sumTimeGrainRateMonth = dayReporterService.getSumTimeGrainRateMonthById(deviceEntity.getId(), lastMonth);
                deviceReporterMonth.setTimeGrainMoveRate(sumTimeGrainRateMonth);

                reporterMonths.add(deviceReporterMonth);
            }

            reporterService.saveBatch(reporterMonths);
        }
    }


    /**
     * 设备指标计算，每小时一次
     *
     * @return
     */
    //@Scheduled(cron = "0 */1 * * * ? ")
    //@Scheduled(cron = "0 0 * * * ?")
    public void calacMetricsOfHour() {
        List<DeviceEntity> deviceList = deviceService.getAllList();
        if(null != deviceList && deviceList.size() > 0){

            Date dtNow = new Date();
            Calendar cal = Calendar.getInstance();
            cal.setTime(dtNow);
            cal.set(Calendar.MILLISECOND,0);
            cal.set(Calendar.MINUTE,0);
            cal.set(Calendar.SECOND,0);

            for(DeviceEntity deviceEntity : deviceList){
                //不保留小数位
                DecimalFormat df = new DecimalFormat("#");
                //合格率 合格数量/总数量
                double quantityRate = (deviceEntity.getTotalCount()==null || deviceEntity.getTotalCount()==0)?0:(1.0*deviceEntity.getGoodCount())/deviceEntity.getTotalCount();

                //性能开动率 标准节拍(配置参数值，可由mes传过来)/（运行时间/生产产量）*100%
                double setPerformance = deviceEntity.getDeviceMeter();//做成配置文件参数  单位：S
                //取当前设备当天的所有运行时长(单位：秒)

                int sumRunning = deviceStatusTimeLineService.getSumRunningTimeDay(deviceEntity.getId());
                double performanceRate = 0;
                if(deviceEntity.getTotalCount()==null || deviceEntity.getTotalCount()==0 || sumRunning==0){
                    performanceRate = 0;
                }else{
                    performanceRate = (1.0*setPerformance)/ (1.0 * sumRunning / deviceEntity.getTotalCount());
                }

                //时间开动率
                //取当前设备当天的所有记录时长(单位：秒)
                //fixed: 修复pg库乱码问题
                int sumTime = deviceStatusTimeLineService.getSumTime(deviceEntity.getId());
                double availability = 0;
                if(sumTime > 0){
                    availability = (1.0*setPerformance)/sumTime;
                }

                //oee
                double oeeRate = quantityRate * performanceRate * availability;

                DeviceMetricsLogEntity metricsLogEntity = new DeviceMetricsLogEntity();
                metricsLogEntity.setId(UUID.randomUUID().toString());
                metricsLogEntity.setDeviceId(deviceEntity.getId());
                metricsLogEntity.setQuality(quantityRate <= 0 ? "0" : df.format(quantityRate*100));
                metricsLogEntity.setPerformance(performanceRate <=0 ? "0" : df.format(performanceRate*100));
                metricsLogEntity.setAvailability(availability <= 0 ? "0" : df.format(availability*100));
                metricsLogEntity.setOee(oeeRate <= 0 ? "0" : df.format(oeeRate*100));

                metricsLogEntity.setGoodCount(deviceEntity.getGoodCount());
                metricsLogEntity.setBadCount(deviceEntity.getBadCount());
                metricsLogEntity.setTotalCont(deviceEntity.getTotalCount());
                metricsLogEntity.setCreateTime(cal.getTime());

                metricsLogEntity.setType("hour");

                log.info("#### 保存的数据：{}", JSONObject.toJSONString(metricsLogEntity));
                deviceMetricsLogService.save(metricsLogEntity);
            }
        }
    }

}
